# sklearn是学习人工智能技术技术必须掌握的库，其中有一份糖尿病患者的数据sklearn.datasets中的load_diabetes，请按照以下要求，正确实现线性回归：
# 1)	导入相关的库，包括但不限于sklearn，numpy等等
import numpy as np
from sklearn.datasets import load_diabetes
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

np.random.seed(123)

# 2)	从sklearn库中加载相应的数据
data = load_diabetes()

# 3)	打印输出数据中的键
print('打印输出数据中的键:',data .keys())

# 4)	正确取出data和target中的数据分别作为X和Y
x= data.data
y = data.target.reshape(-1,1)

# 5)	打印输出X，Y数据维度，并打印出前5个X,Y数据
print('打印输出X数据维度:',x.shape)
print('打印输出Y数据维度:',y.shape)
print('打印出前5个X数据:',x[:5])
print('打印出前5个Y数据:',y[:5])

# 6)	仅取出X数据中的一个特征做训练
data = np.c_[x,y]  #按行连接两个矩阵，就是把两矩阵左右相加，要求行数相等
df = pd.DataFrame(data) #DataFrame表格。
sns.heatmap(df.corr(),annot=True) #返回值：各类型之间的相关系数DataFrame表格。
plt.show()
x = x[:,2:3]

# 7)	切分训练集，测试集
ss = StandardScaler() #数据标准化
x = ss.fit_transform(x)
y = ss.fit_transform(y)
x_train,x_test,y_train,y_test = train_test_split(x,y,train_size=0.7)

# 8)	编写一个类，需要包含：代价函数，训练函数，测试函数
class MyModel():
    def __init__(self,x_train,y_train,iter=10000,alpha=0.01):
        self.x_train = x_train
        self.y_train = y_train
        self.iter = iter
        self.alpha = alpha

        self.m,self.n = x_train.shape
        self.w = np.random.randn(self.n,1)
        self.b = np.random.randn(1)
        self.J = np.zeros(iter)

    # 9)    代价函数使用MSE均方误差
    def my_cost(self,h, y):
        return 1 / (2 * self.m) * (h - y).T.dot(h - y)

    # 11)    在类中编写出预测的方法
    def my_predict(self,x,w,b):
        return x.dot(w)+b

    # 10)    正确在类中编写训练的函数，设置迭代次数为20000，学习率为0.1
    def my_train(self):
        for i in range(self.iter):
            h_train = self.my_predict(self.x_train, self.w,self.b)
            self.J[i] = self.my_cost(h_train, self.y_train)

            dw = 1 / self.m * self.x_train.T.dot(h_train - self.y_train)
            self.w -= self.alpha * dw

            db = 1 / self.m * np.sum(h_train - self.y_train)
            self.b -= self.alpha * db

            # 13)    每500次打印，并输出代价
            if i%500==0:
                print(f'迭代次数:{i},代价值:{self.J[i]:.5f}')

        return self.J, self.w,self.b



if __name__ == '__main__':
    # 12)    实例化该类，并进行训练
    model = MyModel(x_train,y_train)
    J,w,b = model.my_train()
    print(f'权重:{w},偏置:{b}')

    # 14)    绘出训练的学习曲线
    plt.plot(J)
    plt.show()

    # 15)	绘出模型预测值和真实值的散点图
    h_test = model.my_predict(x_test,w,b)

    plt.scatter(x_test, y_test, c='g')
    plt.plot(x_test, h_test, c='r')
    plt.show()

    # 16)	进行模型评估，计算R2值
    mse = np.mean(np.square(y_test - h_test))
    ysigma = np.mean(np.square((y_test - np.mean(y_test))))
    R2 = 1 - mse / ysigma
    print("R2:", R2)